#include "tcp_server.h"

namespace auroral { namespace net {

TcpSession::TcpSession(boost::asio::io_service& io_service)
	: socket_(io_service),
	writting(false),
	strand_(io_service),
	parentServer_(NULL)
{

};

TcpSession::~TcpSession()
{

};

void TcpSession::async_write(const std::string &message)
{
	strand_.dispatch(boost::bind(&TcpSession::push_write_queue, shared_from_this(), message));
}


tcp::socket& TcpSession::socket()
{
	return socket_;
}

void TcpSession::start()
{
	boost::asio::async_read_until(socket_, response_, "\r\n",
		boost::bind(&TcpSession::handle_read, shared_from_this(),
		boost::asio::placeholders::error));
}

void TcpSession::handle_read(const boost::system::error_code& err)
{
	if (!err)
	{
		process_read();
		boost::asio::async_read_until(socket_, response_, "\r\n",
			boost::bind(&TcpSession::handle_read, shared_from_this(),
			boost::asio::placeholders::error));
	}
	else
	{
		// you may need to overloaded this function to do something difference.
		std::cerr << "Error: " << err.message() << "\n";
		std::cerr << "TcpSession exit" << "\n";		
	}
}

void TcpSession::handle_write(const boost::system::error_code& err/*error*/,
				  size_t size/*bytes_transferred*/)
{
	if (!err)
	{
		messages_.pop();
		if (messages_.empty())
		{
			writting = false;
		}
		else
		{
			boost::asio::async_write(socket_, boost::asio::buffer(messages_.front()),
				strand_.wrap(
				boost::bind(&TcpSession::handle_write, shared_from_this(),
				boost::asio::placeholders::error,
				boost::asio::placeholders::bytes_transferred)));
		}
	}
	else
	{
		writting = false;
		//std::cerr << "Error: " << err.message() << "\n";
	}
}



void TcpSession::push_write_queue(const std::string message)
{
	// this method should be run in @strand_.
	messages_.push(message);
	if(!writting && socket_.is_open())
	{
		writting = true;
		boost::asio::async_write(socket_, boost::asio::buffer(messages_.front()),
			strand_.wrap(
			boost::bind(&TcpSession::handle_write, shared_from_this(),
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred)));
	}
	// if the socket is not open, handle_connect() should run async_write instead.
}


//************************************** class TcpServer **********************************************

TcpServer::TcpServer(boost::asio::io_service& io_service, TcpSession::pointer TcpSession, int16_t port)
	: acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
	,TcpSession_(TcpSession)
{
	start_accept();
};

TcpServer::~TcpServer()
{

};

void TcpServer::start_accept()
{
	TcpSession::pointer new_connection =
		TcpSession_->clone();

	acceptor_.async_accept(new_connection->socket(),
		boost::bind(&TcpServer::handle_accept, this, new_connection,
		boost::asio::placeholders::error));
};

void TcpServer::handle_accept(TcpSession::pointer new_connection,
	const boost::system::error_code& error)
{
	if (!error)
	{
		new_connection->start();
		start_accept();
	}
};

} }; // namespace auroral::net